package com.taskinception;

import android.app.Application;
import android.content.Context;
import android.util.Log;

import com.taskinception.connectivity.DatabaseAdapter;
import com.taskinception.model.Task;

import java.util.ArrayList;

/**
 * Application for holding and storing important objects for Task Inception.
 * 
 * @author Jan Svehla
 * 
 */
public class TIApplication extends Application {

    private static Context context;
    private static TIApplication singleton;
    private static DatabaseAdapter databaseAdapter;
    private static Task rootTask;

    public TIApplication() {
	// TIApplication.context = getApplicationContext();
    }

    /**
     * Sets context which will be used.
     * 
     * @param context
     *            context
     */
    public static void setContext(Context context) {
	TIApplication.context = context;
    }

    /**
     * Singleton getter.
     * 
     * @return instance of TIApplication
     */
    public static TIApplication getInstance() {
	if (singleton == null) {
	    singleton = new TIApplication();
	}
	return singleton;
    }

    /**
     * Returns reference to database adapter.
     * 
     * @return database adapter
     */
    public DatabaseAdapter getDatabaseAdapter() {
	if (TIApplication.databaseAdapter == null) {
	    if (context == null) {
		Log.e("TIApplication", "getDatabaseAdapter: context is null!");
		return null;
	    }
	    TIApplication.databaseAdapter = new DatabaseAdapter(context);
	    TIApplication.databaseAdapter.open();
	}
	return TIApplication.databaseAdapter;
    }

    /**
     * Loads root task from database.
     */
    public void reloadRootTaskFromDatabase() {
	TIApplication.rootTask = getDatabaseAdapter().fetchTask(0);
    }

    /**
     * Returns root task of application.
     * 
     * @return root task
     */
    public Task getRootTask() {
	return TIApplication.rootTask;
    }

    /**
     * Sets root task of application.
     * 
     * @param rootTask
     *            root task
     */
    public void setRootTask(Task rootTask) {
	TIApplication.rootTask = rootTask;
    }

    /**
     * Tries to find task with specified ID in whole task tree. Recursively
     * calls itself for every child task.
     * 
     * @param id
     *            id of searched task
     * @return found task or null
     */
    public Task getTask(long id) {
	if (TIApplication.rootTask != null) {
	    return getTask(id, rootTask);
	}

	return null;
    }

    /**
     * Tries to find task with specified ID in task tree. Recursively calls
     * itself for every child task.
     * 
     * @param id
     *            id of searched task
     * @param rootTask
     *            root task from where we'll search
     * @return found task or null
     */
    private Task getTask(long id, Task rootTask) {
	if (rootTask.getId() == id) {
	    return rootTask;
	}

	Task task;
	for (Task child : rootTask.getChildren()) {
	    if ((task = getTask(id, child)) != null) {
		return task;
	    }
	}

	return null;
    }

    /**
     * Inserts task into task tree and database. It does NOT insert its children
     * into database.
     * 
     * @param task
     *            task to insert
     * @param parent
     *            parent where task will be assigned
     * @return success
     */
    public boolean insertTaskIntoTaskTree(Task task, Task parent) {
	if (task == null || parent == null) {
	    return false;
	}

	return insertTaskIntoTaskTree(task, parent.getId());
    }

    /**
     * Inserts task into task tree and database. It does NOT insert its children
     * into database.
     * 
     * @param task
     *            task to insert
     * @param parentID
     *            id of parent where task will be assigned
     * @return success
     */
    public boolean insertTaskIntoTaskTree(Task task, long parentID) {
	Task parent = getTask(parentID);
	if (task == null || parent == null) {
	    return false;
	}

	ArrayList<Task> childrenArrayList = parent.getChildren();
	childrenArrayList.add(task);
	parent.setChildren(childrenArrayList);

	return (getDatabaseAdapter().createTask(task) > 0 && getDatabaseAdapter()
		.updateTask(parent));
    }

    /**
     * Inserts task into task tree and database. It also inserts its children
     * into database.
     * 
     * @param task
     *            task to insert
     * @param parentID
     *            id of parent where task will be assigned
     * @return success
     */
    public boolean insertTaskWithChildrenIntoTaskTree(Task task, long parentID) {
	Task parent = getTask(parentID);
	if (task == null || parent == null) {
	    return false;
	}

	ArrayList<Task> childrenArrayList = parent.getChildren();
	childrenArrayList.add(task);
	parent.setChildren(childrenArrayList);

	getDatabaseAdapter().createTask(task);
	getDatabaseAdapter().updateTask(parent);

	for (Task childTask : task.getChildren()) {
	    insertTaskWithChildrenIntoTaskTree(childTask, task.getId());
	}

	return true;
    }

    /**
     * Deletes task from task tree and database. It also removes reference from
     * parent's children list.
     * 
     * @param task
     *            task to delete
     * @return success
     */
    public boolean deleteTaskFromTaskTree(Task task) {
	if (task == null) {
	    return false;
	}

	return deleteTaskFromTaskTree(task.getId());
    }

    /**
     * Deletes task from task tree and database. It also removes reference from
     * parent's children list.
     * 
     * @param taskID
     *            id of task to delete
     * @return success
     */
    public boolean deleteTaskFromTaskTree(long taskID) {
	Task task = getTask(taskID);
	if (task == null) {
	    return false;
	}
	Task parent = getTask(task.getParent());
	if (parent == null) {
	    return false;
	}

	ArrayList<Task> childrenArrayList = parent.getChildren();
	childrenArrayList.remove(task);
	parent.setChildren(childrenArrayList);

	return (getDatabaseAdapter().updateTask(parent) && getDatabaseAdapter()
		.deleteTask(taskID));
    }

    /**
     * Updates attributes of {@link com.taskinception.model.Task}. If parent
     * Task is different, removes this task from old parent's children list and
     * adds to new parent's list. Does NOT update children and children list!
     * 
     * @param newTask
     *            task to update
     * @return success
     */
    public boolean updateTaskInTaskTree(Task newTask) {
	if (newTask == null) {
	    return false;
	}

	return updateTaskInTaskTree(newTask, newTask.getId());
    }

    /**
     * Updates attributes of task in task tree and database. If parent Task is
     * different, removes this task from old parent's children list and adds to
     * new parent's list. Does NOT update children and children list!
     * 
     * @param newTask
     *            new task
     * @param oldTaskID
     *            id of old task
     * @return success
     */
    public boolean updateTaskInTaskTree(Task newTask, long oldTaskID) {
	if (newTask == null) {
	    return false;
	}
	Task oldTask = getTask(oldTaskID);
	if (oldTask == null) {
	    return false;
	}

	// old task is not simply replaced so the references are not lost
	oldTask.setTitle(newTask.getTitle());
	oldTask.setBody(newTask.getBody());
	oldTask.setCreationDate(newTask.getCreationDate());
	oldTask.setLastModifiedDate(newTask.getLastModifiedDate());
	oldTask.setLastModifier(newTask.getLastModifier());
	oldTask.setDone(newTask.isDone());
	oldTask.setSharedWith(newTask.getSharedWith());
	// oldTask.setRemoteId(newTask.getRemoteId());
	oldTask.setOwner(newTask.getOwner());

	if (oldTask.getParent() != newTask.getParent()) {
	    Task parent = getTask(oldTask.getParent());
	    ArrayList<Task> childrenArrayList = parent.getChildren();
	    childrenArrayList.remove(oldTask);
	    parent.setChildren(childrenArrayList);

	    parent = getTask(newTask.getParent());
	    childrenArrayList = parent.getChildren();
	    childrenArrayList.add(oldTask);
	    parent.setChildren(childrenArrayList);
	}

	return getDatabaseAdapter().updateTask(oldTask);
    }
}
